package sub1;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Timer;

/**
 * Create a thread to handle each new connection
 * @author Magali Moulin, Pierre Labiausse
 * @version 1
 */
public class Server {
	private ServerSocket serverSocket;
	private HashMap <String,ArrayList<ClientTcpTuple>> directory=new HashMap<String, ArrayList<ClientTcpTuple>>();
	private ArrayList<ClientTcpTuple> clientRegister=new ArrayList<ClientTcpTuple>();
	private ServerHandler handler ;
	
	/**
	 * Main class on the server side, set the server port at the chosen value or at the default value and create a new server.
	 * @param args
	 */
	public static void main(String[] args){
		int portServer = 0;
		if(args.length > 0){
			try {
				portServer = Integer.parseInt(args[0]);
			} catch (NumberFormatException e){
				System.err.println("Port given is not a number");
			}
		}
		else {
			portServer = Configuration.DEFAULT_PORT;
		}
		
		new Server(portServer);
	}
	
	/**
	 * Constructor of this class which takes the port of the server in parameter and listen for connection with clients
	 * @param port: port of the server
	 */
	public Server(int port){
		try {
			serverSocket = new ServerSocket(port);
		} catch (IOException e) {
			System.err.println("Could not listen on port: "+port+".");
			System.exit(1);
		}
		while(true){
			Socket clientSocket = null;
			try {
				System.out.println("Server : .accept()");
				clientSocket = serverSocket.accept();
				System.out.println("New client connection");
			} catch (IOException e) {
				e.printStackTrace();
			}
			handler = new ServerHandler(clientSocket,this);
			handler.start();
		}
	}
	
	/**
	 * Add a new client on the list of registered clients on the server
	 * @param clientTcpTuple: the client who wants to be registered
	 * @param sharedFiles: the list of shared files by this client
	 */
	public synchronized void addnewClient(ClientTcpTuple clientTcpTuple, LinkedList<String> sharedFiles){
		clientRegister.add(clientTcpTuple);
		for( String filename : sharedFiles){
			if (directory.containsKey(filename)){
				directory.get(filename).add(clientTcpTuple);
			}
			else{
				ArrayList<ClientTcpTuple> tmp = new ArrayList<ClientTcpTuple>(); 
				tmp.add(clientTcpTuple);
				directory.put(filename, tmp);
			}
		}		
	}
	
	/**
	 * Remove client from the list of registered clients on the server and updates the directory
	 * @param clientTcpTuple: client who wants to be remove
	 */
	public synchronized void removeClient(ClientTcpTuple clientTcpTuple){
		clientRegister.remove(clientTcpTuple);
		for(ArrayList<ClientTcpTuple> tmp:directory.values()){
			System.out.println(tmp.size());
			tmp.remove(clientTcpTuple);
			System.out.println(tmp.size());
		}
	}
	
	/**
	 * Getter to have access to the directory on the server
	 * @return directory
	 */
	public synchronized  HashMap<String, ArrayList<ClientTcpTuple>> getDirectory(){
	return directory;
	}
	
	/**
	 * Getter to have access to the list of registered clients on the server
	 * @return clientRegister: the list of registered clients
	 */
	public synchronized ArrayList<ClientTcpTuple> getlistRegisterClient(){
		return clientRegister;
	}
	
	/**
	 * Search in the directory of the server
	 * @param request : the searched keyword
	 * @return result : the HashMap which contains all the files and clients who shared these files corresponding to the request
	 */
	public synchronized HashMap<String, ArrayList<ClientTcpTuple>> searchDirectory(String request){
		System.out.println("New search for keyword : "+request);
		HashMap<String, ArrayList<ClientTcpTuple>> result = new HashMap<String, ArrayList<ClientTcpTuple>>();
		for(String filename : directory.keySet()){
			if(filename.contains(request)){
				System.out.println("Corresponding file : "+filename);
				result.put(filename, directory.get(filename));
			}
		}
		return result;
	}
}


